﻿namespace PowerSong.UserInterface
    {
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Media;
    using PowerSong.Projection;
    using PowerSong.SongDatabase;

    /// <summary>
    /// A projection mode.
    /// </summary>
    public enum ProjectorMode
        {

        /// <summary>
        /// Show nothing.
        /// </summary>
        Black,

        /// <summary>
        /// Show the background colour/image/video only.
        /// </summary>
        Background,

        /// <summary>
        /// Show everything.
        /// </summary>
        Full

        }

    /// <summary>
    /// This class is class that deals with presenting content onto zero or more projection screens using PowerSong APA.
    /// All communication to the PowerSong projectors should be via this class.
    /// </summary>
    public class Theatre
        {

        private enum BackgroundType
            {
            SolidColour,
            Image,
            Video
            }

        private enum BackgroundOverrideType
            {
            None,
            OverrideColour,
            OverrideImage,
            OverrideVideo
            }

        private ProjectorSet projectors = new ProjectorSet();

        private BackgroundType backgroundType = BackgroundType.SolidColour;

        private ProjectorMode mode = ProjectorMode.Black;

        private PowerSong.SongDatabase.Style style = StyleManager.CreateDefault("Default");

        private BackgroundOverrideType backgroundOverrideType = BackgroundOverrideType.None;

        private Color backgroundOverrideColour = Colors.Black;

        private string backgroundOverrideFileName = null;

        private string activeTitle = string.Empty;

        private string[] activeVerses = null;

        public int VerseIndex { get; private set; }

        private string activeCopyright = string.Empty;

        private bool videoPaused = true;

        public Theatre()
            {
            this.VerseIndex = -1;
            }

        /// <summary>
        /// Gets the number of projectors.
        /// </summary>
        /// <returns>The number of projectors.</returns>
        public int ProjectorCount()
            {
            return projectors.AllProjectors.Length;
            }

        /// <summary>
        /// Creates a new projector, that will be rendered onto the specified Grid control.
        /// </summary>
        /// <param name="grid">The grid on which to render the projection.</param>
        /// <returns>A key that can be used to later manage the created projector.</returns>
        public int CreateProjector(Grid grid)
            {
            var newProjector = projectors.CreateControlProjector(grid);
            PushProjectlets(newProjector);
            ConfigureProjector(newProjector);
            return Array.IndexOf<Projector>(projectors.AllProjectors, newProjector);
            }

        /// <summary>
        /// Creates a new projector, that will be rendered onto a new window.
        /// The window is created and displayed immediately.
        /// </summary>
        /// <returns>A key that can be used to later manage the created projector.</returns>
        public int CreateProjector()
            {
            var newProjector = projectors.CreateWindowProjector(true);
            PushProjectlets(newProjector);
            ConfigureProjector(newProjector);
            return Array.IndexOf<Projector>(projectors.AllProjectors, newProjector);
            }

        /// <summary>
        /// Removes the projector having the specified key.
        /// </summary>
        /// <param name="projectorID">The projector to remove.</param>
        public void RemoveProjector(int projectorID)
            {
            var projector = projectors.AllProjectors[projectorID];
            projectors.RemoveProjector(projector);
            }

        /// <summary>
        /// Removes all projectors.
        /// </summary>
        public void RemoveAllProjectors()
            {
            projectors.RemoveAllProjectors();
            }

        public void ChangeMode(ProjectorMode mode)
            {

            // Set the mode
            if (mode == this.mode) return;
            this.mode = mode;

            // Fade all projectors' projectlets in and/or out depending on the new mode
            foreach (var projector in projectors.AllProjectors)
                {
                if (mode == ProjectorMode.Black)
                    {
                    projector.FadeOutAll();
                    }
                else if (mode == ProjectorMode.Background)
                    {
                    projector.FadeOutAll(ProjectletType.Text);
                    if (backgroundType == BackgroundType.SolidColour) projector.FadeIn("BackgroundColour");
                    if (backgroundType == BackgroundType.Image) projector.FadeIn("BackgroundImage");
                    if (backgroundType == BackgroundType.Video) projector.FadeIn("BackgroundVideo");
                    }
                else
                    {
                    projector.FadeInAll(ProjectletType.Text);
                    if (backgroundType == BackgroundType.SolidColour) projector.FadeIn("BackgroundColour");
                    if (backgroundType == BackgroundType.Image) projector.FadeIn("BackgroundImage");
                    if (backgroundType == BackgroundType.Video) projector.FadeIn("BackgroundVideo");
                    }                
                }

            }

        public void ResetOverride()
            {
            this.backgroundOverrideType = BackgroundOverrideType.None;
            foreach (var projector in projectors.AllProjectors)
                {
                ChangeStyleInternal(projector, style);
                }
            }

        public void OverrideBackground(Color color)
            {
            this.backgroundOverrideType = BackgroundOverrideType.OverrideColour;
            this.backgroundOverrideColour = color;
            foreach (var projector in projectors.AllProjectors)
                {
                ChangeBackgroundInternal(projector, color);
                }
            }

        public void OverrideBackground(string imageFileName)
            {
            this.backgroundOverrideType = BackgroundOverrideType.OverrideImage;
            this.backgroundOverrideFileName = imageFileName;
            foreach (var projector in projectors.AllProjectors)
                {
                ChangeBackgroundInternal(projector, imageFileName);
                }
            }

        public void OverrideBackground(string videoFileName,
                                       TimeSpan startPosition,
                                       double volume,
                                       double speed,
                                       bool loop)
            {
            this.backgroundOverrideType = BackgroundOverrideType.OverrideVideo;
            this.backgroundOverrideFileName = videoFileName;
            foreach (var projector in projectors.AllProjectors)
                {
                ChangeBackgroundInternal(projector, videoFileName, startPosition, volume, speed, loop);
                }
            }

        public void ChangeStyle(PowerSong.SongDatabase.Style style)
            {
            this.style = style;
            foreach (var projector in projectors.AllProjectors)
                {
                ChangeStyleInternal(projector, style);
                }
            }

#if !TESTER

        public void SetStyle(IStyledServiceItem styledItem)
            {
            var tempStyle = new SongDatabase.Style();
            styledItem.Style.CopyTo(tempStyle);
            if (styledItem.StyleOverride != null)
                {
                if (styledItem.StyleOverride.Colour != null) tempStyle.BackgroundColor = styledItem.StyleOverride.Colour.Color.ToUInt();
                if (styledItem.StyleOverride.ImageFileName != null) tempStyle.BackgroundFileName = styledItem.StyleOverride.ImageFileName;
                if (styledItem.StyleOverride.VideoFileName != null) tempStyle.BackgroundFileName = styledItem.StyleOverride.VideoFileName;
                }
            ChangeStyle(tempStyle);
            }

#endif

        public void ChangeText(string title, string[] verses, string copyright)
            {

            // Set class variables
            this.activeTitle = title;
            this.activeVerses = verses;
            this.VerseIndex = 0;
            this.activeCopyright = copyright;

            // Set the text of the text projectlets
            foreach (var projector in projectors.AllProjectors)
                {
                projector.SetText("Title", title);
                projector.SetText("Copyright", copyright);
                projector.SetText("Verse", verses[VerseIndex]);
                }            

            }

        public void ChangeVerse(int verseIndex)
            {

            // Change the active verse index
            var previousVerseIndex = verseIndex;
            this.VerseIndex = verseIndex;

            // Show the verse text
            foreach (var projector in projectors.AllProjectors)
                {
                var verseText = activeVerses[verseIndex];
                if (previousVerseIndex == -1 || mode != ProjectorMode.Full)
                    {
                    projector.SetText("Verse", verseText);
                    }
                else
                    {
                    projector.CrossFadeText("Verse", verseText);
                    }
                }

            }

        public void PlayVideo(string fileName, TimeSpan startPosition, double volume, double speed, bool loop)
            {
            foreach (var projector in projectors.AllProjectors)
                {

                // Set up new video for playing if we aren't simply paused
                if (!videoPaused)
                    {
                    projector.FadeIn("BackgroundVideo");
                    projector.SetVideoDetails("BackgroundVideo", fileName, startPosition, volume, speed, loop);
                    }

                // Play video
                projector.PlayVideo("BackgroundVideo");

                // Fade out everything else
                foreach (var projectlet in projector.Projectlets)
                    {
                    if (projectlet.Type != ProjectletType.Video) projector.FadeOut(projectlet.Name);
                    }

                // We are certainly not paused!
                videoPaused = false;

                }
            }
        
        public void StopVideo()
            {
            foreach (var projector in projectors.AllProjectors)
                {

                // Stop and fade out the video
                projector.StopVideo("BackgroundVideo");
                projector.FadeOut("BackgroundVideo");

                // Fade in everything else
                foreach (var projectlet in projector.Projectlets)
                    {
                    if (projectlet.Type != ProjectletType.Video) projector.FadeIn(projectlet.Name);
                    }

                // We certainly aren't paused!
                videoPaused = false;

                }
            }

        public void PauseVideo()
            {
            foreach (var projector in projectors.AllProjectors)
                {
                projector.PauseVideo("BackgroundVideo");
                videoPaused = true;
                }
            }

        #region Helper Methods

        private void PushProjectlets(Projector projector)
            {

            // Create all of the necessary projectlets, pushing them onto the projector
            projector.PushProjectlet(ProjectletType.Rectangle, "BackgroundColour");
            projector.PushProjectlet(ProjectletType.Image, "BackgroundImage");
            projector.PushProjectlet(ProjectletType.Video, "BackgroundVideo");
            projector.PushProjectlet(ProjectletType.Text, "Copyright");
            projector.PushProjectlet(ProjectletType.Text, "Title");
            projector.PushProjectlet(ProjectletType.Text, "Verse");

            }

        private void ConfigureProjector(Projector projector)
            {

            // Configure the style
            ChangeStyleInternal(projector, style);

            // Configure style override
            if (backgroundOverrideType == BackgroundOverrideType.OverrideColour)
                {
                ChangeBackgroundInternal(projector, backgroundOverrideColour);
                }
            else if (backgroundOverrideType == BackgroundOverrideType.OverrideImage)
                {
                ChangeBackgroundInternal(projector, backgroundOverrideFileName);
                }
            else if (backgroundOverrideType == BackgroundOverrideType.OverrideVideo)
                {
                ChangeBackgroundInternal(projector, backgroundOverrideFileName, TimeSpan.FromSeconds(0), 0, 1, true);
                }

            // Change content of text projectlets
            projector.SetText("Copyright", activeCopyright);
            projector.SetText("Title", activeTitle);
            projector.SetText("Verse", VerseIndex == -1 ? string.Empty : activeVerses[VerseIndex]);

            // Ensure the projector is in the correct mode
            if (mode == ProjectorMode.Black || mode == ProjectorMode.Background)
                {
                projector.SetOpacity("Copyright", 0);
                projector.SetOpacity("Title", 0);
                projector.SetOpacity("Verse", 0);
                }
            if (mode == ProjectorMode.Black)
                {
                projector.SetOpacityAll(0);
                }

            }

        private void ChangeStyleInternal(Projector projector, PowerSong.SongDatabase.Style style)
            {

            // Set backgrounds
            projector.SetBackground("BackgroundColour", style.BackgroundColor.ToColor());
            if (!string.IsNullOrEmpty(style.BackgroundFileName))
                {
                projector.SetImage("BackgroundImage", style.BackgroundFileName);
#warning Need ability to set video backgrounds as well
                }

            // Set the fade times for the non-textual projectlets
#warning Where are the fade times going to come from?
            projector.GetProjectlet("BackgroundColour").FadeInTime = TimeSpan.FromSeconds(2);
            projector.GetProjectlet("BackgroundImage").FadeInTime = TimeSpan.FromSeconds(2);
            projector.GetProjectlet("BackgroundVideo").FadeInTime = TimeSpan.FromSeconds(2);

            // Set projectlet styles
            foreach (var name in new string[] { "Title", "Verse", "Copyright" })
                {
                var subStyle = style.GetSubStyle(name);
                var projectlet = projector.GetProjectlet(name);

                // Set the margin
                projector.SetMargin(name, subStyle.Margin.Left, subStyle.Margin.Top, subStyle.Margin.Right, subStyle.Margin.Bottom);

                // Determine the text alignment
                var textAlignment = TextAlignment.Left;
                if (subStyle.TextAlignment == Alignment.CenterTop ||
                    subStyle.TextAlignment == Alignment.CenterMiddle ||
                    subStyle.TextAlignment == Alignment.CenterBottom) textAlignment = TextAlignment.Center;
                if (subStyle.TextAlignment == Alignment.RightTop ||
                    subStyle.TextAlignment == Alignment.RightMiddle ||
                    subStyle.TextAlignment == Alignment.RightBottom) textAlignment = TextAlignment.Right;

                // Set the main aspects of the substyle
                projector.SetStyle(name,
                                   subStyle.FontEffect == TextEffectType.DrawRectangle ? new SolidColorBrush(subStyle.FontEffectColor.ToColor()) : null,
                                   new SolidColorBrush(subStyle.FontColor.ToColor()),
                                   subStyle.FontName,
                                   textAlignment,
                                   TextWrapping.Wrap,
                                   subStyle.FontSize,
                                   (subStyle.FontStyle & FontEffectType.Bold) == FontEffectType.Bold ? FontWeights.Bold : FontWeights.Normal,
                                   (subStyle.FontStyle & FontEffectType.Italic) == FontEffectType.Italic ? FontStyles.Italic : FontStyles.Normal,
                                   (subStyle.FontStyle & FontEffectType.Underline) == FontEffectType.Underline ? TextDecorations.Underline : null);

                // Set alignment
                SetAlignment(projector, name, subStyle.TextAlignment);

                // Set fade times
                projectlet.FadeInTime = subStyle.FadeIn.Type == FadeType.None ? new TimeSpan(1) : TimeSpan.FromMilliseconds(subStyle.FadeIn.Duration);
                projectlet.FadeOutTime = subStyle.FadeOut.Type == FadeType.None ? new TimeSpan(1) : TimeSpan.FromMilliseconds(subStyle.FadeOut.Duration);
                projectlet.CrossFadeTime = subStyle.CrossFade.Type == FadeType.None ? new TimeSpan(1) : TimeSpan.FromMilliseconds(subStyle.CrossFade.Duration);

                }

            }

        private void SetAlignment(Projector projector, string projectletName, Alignment alignment)
            {

            // These are the defaults
            var horizontalAlignment = HorizontalAlignment.Center;
            var verticalAlignment = VerticalAlignment.Center;

            // Refine from the defaults
            switch (alignment)
                {

                case Alignment.LeftTop:
                    horizontalAlignment = HorizontalAlignment.Left;
                    verticalAlignment = VerticalAlignment.Top;
                    break;

                case Alignment.LeftMiddle:
                    horizontalAlignment = HorizontalAlignment.Left;
                    break;

                case Alignment.LeftBottom:
                    horizontalAlignment = HorizontalAlignment.Left;
                    verticalAlignment = VerticalAlignment.Bottom;
                    break;

                case Alignment.CenterTop:
                    verticalAlignment = VerticalAlignment.Top;
                    break;

                case Alignment.CenterBottom:
                    verticalAlignment = VerticalAlignment.Bottom;
                    break;

                case Alignment.RightTop:
                    horizontalAlignment = HorizontalAlignment.Right;
                    verticalAlignment = VerticalAlignment.Top;
                    break;

                case Alignment.RightMiddle:
                    horizontalAlignment = HorizontalAlignment.Right;
                    break;

                case Alignment.RightBottom:
                    horizontalAlignment = HorizontalAlignment.Right;
                    verticalAlignment = VerticalAlignment.Bottom;
                    break;

                }

            // Set the alignment on the projector itself
            projector.SetAlignment(projectletName, horizontalAlignment, verticalAlignment);

            }

        private void ChangeBackgroundInternal(Projector projector, Color color)
            {
            var oldBackgroundType = backgroundType;
            backgroundType = BackgroundType.SolidColour;
            projector.StopVideo("BackgroundVideo");
            projector.SetBackground("BackgroundColour", color);
            if (mode != ProjectorMode.Black)
                {
                FadeBackgrounds(oldBackgroundType, backgroundType);
                }
            }

        private void ChangeBackgroundInternal(Projector projector, string imageFileName)
            {
            var oldBackgroundType = backgroundType;
            backgroundType = BackgroundType.Image;
            projector.StopVideo("BackgroundVideo");
            projector.SetImage("BackgroundImage", imageFileName);
            if (mode != ProjectorMode.Black)
                {
                FadeBackgrounds(oldBackgroundType, backgroundType);
                }
            }

        private void ChangeBackgroundInternal(Projector projector,
                                              string videoFileName,
                                              TimeSpan startPosition,
                                              double volume,
                                              double speed,
                                              bool loop)
            {
            var oldBackgroundType = backgroundType;
            backgroundType = BackgroundType.Video;
            projector.SetVideoDetails("BackgroundVideo", videoFileName, startPosition, volume, speed, loop);
            projector.PlayVideo("BackgroundVideo");
            if (mode != ProjectorMode.Black)
                {
                FadeBackgrounds(oldBackgroundType, backgroundType);
                }
            }

        private void FadeBackgrounds(BackgroundType oldType, BackgroundType newType)
            {
            foreach (var projector in projectors.AllProjectors)
                {

                if (oldType == BackgroundType.SolidColour)
                    {
                    if (newType == BackgroundType.Image)
                        {
                        projector.SetOpacity("BackgroundColour", 1);
                        projector.FadeIn("BackgroundImage");
                        }
                    else if (newType == BackgroundType.Video)
                        {
                        projector.SetOpacity("BackgroundColour", 1);
                        projector.FadeIn("BackgroundVideo");
                        }
                    }

                if (oldType == BackgroundType.Image)
                    {
                    if (newType == BackgroundType.SolidColour)
                        {
                        projector.SetOpacity("BackgroundColour", 1);
                        projector.FadeOut("BackgroundImage");
                        }
                    else if (newType == BackgroundType.Video)
                        {
                        projector.SetOpacity("BackgroundImage", 1);
                        projector.FadeIn("BackgroundVideo");
                        }
                    }

                if (oldType == BackgroundType.Video)
                    {
                    if (newType == BackgroundType.SolidColour)
                        {
                        projector.SetOpacity("BackgroundColour", 1);
                        projector.SetOpacity("BackgroundImage", 0);
                        projector.FadeOut("BackgroundVideo");
                        }
                    else if (newType == BackgroundType.Image)
                        {
                        projector.SetOpacity("BackgroundImage", 1);
                        projector.FadeOut("BackgroundVideo");
                        }

                    }

                }
            }

        #endregion

        }

    }
